package me.kagami.autotesthelper;


import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;


import java.math.BigDecimal;
import java.util.*;

public class GenerateMapHandler {
    private Project project;
    private PsiClass listClass;
    private PsiClass mapClass;
    private PsiClass setClass;

    private PsiType listType;
    private PsiType mapType;
    private PsiType setType;


    public GenerateMapHandler(Project project) {
        this.project = project;
        listClass = JavaPsiFacade.getInstance(project).findClass(List.class.getCanonicalName(), GlobalSearchScope.allScope(project));
        mapClass = JavaPsiFacade.getInstance(project).findClass(Map.class.getCanonicalName(), GlobalSearchScope.allScope(project));
        setClass = JavaPsiFacade.getInstance(project).findClass(Set.class.getCanonicalName(), GlobalSearchScope.allScope(project));
        listType = JavaPsiFacade.getInstance(project).getElementFactory().createType(listClass);
        mapType = JavaPsiFacade.getInstance(project).getElementFactory().createType(mapClass);
        setType = JavaPsiFacade.getInstance(project).getElementFactory().createType(setClass);
    }

    public Map getMapFromFunction(String className, String function) {
        PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
        PsiMethod[] methods = aClass.getMethods();
        Map result = new LinkedHashMap();
        for (PsiMethod method : methods) {
            if (method.getName().equals(function)) {
                final PsiParameterList parameterList = method.getParameterList();
                final PsiParameter[] parameters = parameterList.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    PsiParameter parameter = parameters[i];
                    Map beanMap = null;
                    try {
                        final PsiClass pClass = JavaPsiFacade.getInstance(project).findClass(parameter.getType().getCanonicalText(), GlobalSearchScope.allScope(project));
                        beanMap = getBeanMap(pClass);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    result.put("arg" + i, beanMap);
                }
            }
        }
        return result;
    }

    public Map getMap(String className) {
        final PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
        return getBeanMap(aClass);
    }

    private Map getBeanMap(PsiClass psiClass) {
        Map map = new LinkedHashMap();
        map.put("class", psiClass.getQualifiedName());
        PsiField[] allFields = psiClass.getAllFields();
        for (PsiField field : allFields) {
            if (isStatic(field)) {
                continue;
            }

            if (field.getType().getCanonicalText().equals("boolean") || field.getType().getCanonicalText().equals(Boolean.class.getCanonicalName())) {
                map.put(field.getName(), true);
            } else if (field.getType().getCanonicalText().equals("byte") || field.getType().getCanonicalText().equals(Byte.class.getCanonicalName())) {
                map.put(field.getName(), new Byte("1"));
            } else if (field.getType().getCanonicalText().equals("short") || field.getType().getCanonicalText().equals(Short.class.getCanonicalName())) {
                map.put(field.getName(), new Short("1"));
            } else if (field.getType().getCanonicalText().equals("int") || field.getType().getCanonicalText().equals(Integer.class.getCanonicalName())) {
                map.put(field.getName(), 1);
            } else if (field.getType().getCanonicalText().equals("long") || field.getType().getCanonicalText().equals(Long.class.getCanonicalName())) {
                map.put(field.getName(), 1L);
            } else if (field.getType().getCanonicalText().equals("float") || field.getType().getCanonicalText().equals(Float.class.getCanonicalName())) {
                map.put(field.getName(), 1F);
            } else if (field.getType().getCanonicalText().equals("double") || field.getType().getCanonicalText().equals(Double.class.getCanonicalName())) {
                map.put(field.getName(), 1D);
            } else if (field.getType().getCanonicalText().equals("char") || field.getType().getCanonicalText().equals(Character.class.getCanonicalName())) {
                map.put(field.getName(), 'a');
            } else if (field.getType().getCanonicalText().equals(String.class.getCanonicalName())) {
                map.put(field.getName(), "string");
            } else if (field.getType().getCanonicalText().equals(BigDecimal.class.getCanonicalName())) {
                map.put(field.getName(), new BigDecimal("1"));
            } else if (field.getType().getCanonicalText().equals(Date.class.getCanonicalName())) {
                map.put(field.getName(), new Date(1111));
            } else if (field.getType() instanceof PsiArrayType) {
                PsiType deepComponentType = field.getType().getDeepComponentType();
                Object o = isCommonType(deepComponentType);
                map.put(field.getName(), new Object[]{o});
            } else {

                if (listType.isAssignableFrom(field.getType())) {
                    PsiType psiType = PsiUtil.extractIterableTypeParameter(field.getType(), false);
                    Object o = isCommonType(psiType);
                    List<Object> value = new ArrayList<>();
                    if (o == null) {
                        PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(psiType.getCanonicalText(), GlobalSearchScope.allScope(project));
                        value.add(getBeanMap(aClass));
                    } else {
                        value.add(o);
                    }
                    map.put(field.getName(), value);
                } else if (mapType.isAssignableFrom(field.getType())) {
                    map.put(field.getName(), new HashMap<>());
                } else if (setType.isAssignableFrom(field.getType())) {
                    PsiType psiType = PsiUtil.extractIterableTypeParameter(field.getType(), false);
                    Object o = isCommonType(psiType);
                    Set<Object> value = new HashSet<>();
                    if (o == null) {
                        PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(psiType.getCanonicalText(), GlobalSearchScope.allScope(project));
                        value.add(getBeanMap(aClass));
                    } else {
                        value.add(o);
                    }

                    map.put(field.getName(), value);
                } else {
                    PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(field.getType().getCanonicalText(), GlobalSearchScope.allScope(project));
                    map.put(field.getName(), getBeanMap(aClass));
                }

            }

        }
        return map;
    }

    private Object isCommonType(PsiType psiType) {
        if (psiType.getCanonicalText().equals("boolean") || psiType.getCanonicalText().equals(Boolean.class.getCanonicalName())) {
            return true;
        } else if (psiType.getCanonicalText().equals("byte") || psiType.getCanonicalText().equals(Byte.class.getCanonicalName())) {
            return new Byte("1");
        } else if (psiType.getCanonicalText().equals("short") || psiType.getCanonicalText().equals(Short.class.getCanonicalName())) {
            return new Short("1");
        } else if (psiType.getCanonicalText().equals("int") || psiType.getCanonicalText().equals(Integer.class.getCanonicalName())) {
            return 1;
        } else if (psiType.getCanonicalText().equals("long") || psiType.getCanonicalText().equals(Long.class.getCanonicalName())) {
            return 1L;
        } else if (psiType.getCanonicalText().equals("float") || psiType.getCanonicalText().equals(Float.class.getCanonicalName())) {
            return 1F;
        } else if (psiType.getCanonicalText().equals("double") || psiType.getCanonicalText().equals(Double.class.getCanonicalName())) {
            return 1D;
        } else if (psiType.getCanonicalText().equals("char") || psiType.getCanonicalText().equals(Character.class.getCanonicalName())) {
            return 'a';
        } else if (psiType.getCanonicalText().equals(String.class.getCanonicalName())) {
            return "string";
        } else if (psiType.getCanonicalText().equals(BigDecimal.class.getCanonicalName())) {
            return new BigDecimal("1");
        } else if (psiType.getCanonicalText().equals(Date.class.getCanonicalName())) {
            return new Date(1111);
        } else {
            return null;
        }
    }

    private boolean isStatic(PsiField field) {
        PsiModifierList modifierList = field.getModifierList();
        if (modifierList == null || !modifierList.hasModifierProperty(PsiModifier.STATIC)) {
            return false;
        }
        return true;
    }

}
